package cn.aitrox.ry.util;

import cn.aitrox.ry.bean.DicomFileAttributes;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Sequence;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomEncodingOptions;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.SafeClose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DicomUtils {

    private static final Logger logger = LoggerFactory.getLogger(DicomUtils.class);

    public static final String DEFAULT_SEPARATOR = "\\";

    /**
     * 超过int的最大值
     */
    private static final List<String> errorTagList = Arrays.asList("(FFFA,FFFA)", "(FFFC,FFFC)", "(FFFE,E000)", "(FFFE,E00D)", "(FFFE,E0DD)");

    /**
     * 加载dcm文件，获取公有Tag 以及私有tag 值
     *
     * @param file
     * @return
     */
    public static DicomFileAttributes loadDcmFile(File file) {
        Boolean dsOnlyTag = false;
        DicomInputStream dcmIS = null;
        // 元tag
        Attributes inFmi;
        // 公有tag
        Attributes inDs;
        try {
            dcmIS = new DicomInputStream(file);
            dcmIS.setAllocateLimit(Integer.MAX_VALUE);
            DicomFileAttributes dicomFileAttributes = new DicomFileAttributes();
            inFmi = dcmIS.getFileMetaInformation();
            try {
                inDs = dcmIS.readDataset(-1, -1);
            } catch (Exception e) {
                // 超过2G文件单独读取tag信息
                dsOnlyTag = true;
                // 需要重置流才能再次读取
                dcmIS.reset();
                inDs = dcmIS.readDataset(-1, Tag.PixelData);
                logger.info(LogUtil.gen("loadDcmFile read tag only", "file", file.getAbsolutePath()));
            }
            dicomFileAttributes.setInFmi(inFmi);
            dicomFileAttributes.setInDs(inDs);
            dicomFileAttributes.setDsOnlyTag(dsOnlyTag);
            if (inFmi != null) {
                int fileMetaInformationGroupLength = inFmi.calcLength(DicomEncodingOptions.DEFAULT, dcmIS.explicitVR());
                dicomFileAttributes.setFileMetaInformationGroupLength(fileMetaInformationGroupLength);
            }
            return dicomFileAttributes;
        } catch (Exception e) {
            logger.error(LogUtil.gen("loadDcmFile fail", "file", file.getAbsolutePath()), e);
            return null;
        } finally {
            SafeClose.close(dcmIS);
        }
    }

    public static DicomFileAttributes loadDcmFileOnlyTag(File file) {
        DicomInputStream dcmIS = null;
        try {
            dcmIS = new DicomInputStream(file);
            DicomFileAttributes dicomFileAttributes = new DicomFileAttributes();
            Attributes inFmi = dcmIS.getFileMetaInformation();
            Attributes inDs = dcmIS.readDataset(-1, Tag.PixelData);
            dicomFileAttributes.setInFmi(inFmi);
            dicomFileAttributes.setInDs(inDs);
            dicomFileAttributes.setDsOnlyTag(true);
            if (inFmi != null) {
                int fileMetaInformationGroupLength = inFmi.calcLength(DicomEncodingOptions.DEFAULT, dcmIS.explicitVR());
                dicomFileAttributes.setFileMetaInformationGroupLength(fileMetaInformationGroupLength);
            }
            return dicomFileAttributes;
        } catch (Exception e) {
            logger.error(LogUtil.gen("loadDcmFile fail", "file", file.getAbsolutePath()), e);
            return null;
        } finally {
            SafeClose.close(dcmIS);
        }
    }

    public static DicomFileAttributes loadDcmByInputStream(InputStream inputStream) {
        Boolean dsOnlyTag = false;
        DicomInputStream dcmIS = null;
        // 元tag
        Attributes inFmi;
        // 公有tag
        Attributes inDs;
        try {
            dcmIS = new DicomInputStream(inputStream);
            dcmIS.setAllocateLimit(Integer.MAX_VALUE);
            DicomFileAttributes dicomFileAttributes = new DicomFileAttributes();
            inFmi = dcmIS.getFileMetaInformation();
            try {
                inDs = dcmIS.readDataset(-1, -1);
            } catch (Exception e) {
                // 超过2G文件单独读取tag信息
                dsOnlyTag = true;
                // 需要重置流才能再次读取
                dcmIS.reset();
                inDs = dcmIS.readDataset(-1, Tag.PixelData);
                logger.info(LogUtil.gen("loadDcmByInputStream read tag only"));
            }
            dicomFileAttributes.setInFmi(inFmi);
            dicomFileAttributes.setInDs(inDs);
            dicomFileAttributes.setDsOnlyTag(dsOnlyTag);
            if (inFmi != null) {
                int fileMetaInformationGroupLength = inFmi.calcLength(DicomEncodingOptions.DEFAULT, dcmIS.explicitVR());
                dicomFileAttributes.setFileMetaInformationGroupLength(fileMetaInformationGroupLength);
            }
            return dicomFileAttributes;
        } catch (Exception e) {
            logger.error(LogUtil.gen("loadDcmByInputStream fail"), e);
            return null;
        } finally {
            if (null != dcmIS) {
                SafeClose.close(dcmIS);
            }
        }
    }

    public static String getTagValueByTagId(String tagCoordinate, Attributes attributes) {
        // 大于integer值
        if (errorTagList.contains(tagCoordinate)) {
            return null;
        }
        String tagCoordinateStr = tagCoordinate.replaceAll("[^\\w-]", "");
        Integer tagKey = Integer.parseInt(tagCoordinateStr, 16);
        return DicomUtils.getTagValue(tagKey, attributes);
    }

    public static String getTagValue(int tag, Attributes attributes) {
        VR vr = attributes.getVR(tag);
        if (vr == null) {
            return null;
        }
        String tagValue = null;
        switch (vr) {
            case AE:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.AE);
                break;
            case AS:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.AS);
                break;
            case CS:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.CS);
                break;
            case DA:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.DA);
                break;
            case DS:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.DS);
                break;
            case DT:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.DT);
                break;
            case IS:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.IS);
                break;
            case LO:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.LO);
                break;
            case LT:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.LT);
                break;
            case PN:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.PN);
                break;
            case SH:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.SH);
                break;
            case ST:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.ST);
                break;
            case TM:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.TM);
                break;
            case UC:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UC);
                break;
            case UI:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UI);
                break;
            case UR:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UR);
                break;
            case UT:
                tagValue = converterStringsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UT);
                break;
            case FL:
                tagValue = converterDoublesToString(tag, attributes, DEFAULT_SEPARATOR, VR.FL);
                break;
            case OF:
                tagValue = converterDoublesToString(tag, attributes, DEFAULT_SEPARATOR, VR.OF);
                break;
            case FD:
                tagValue = converterDoublesToString(tag, attributes, DEFAULT_SEPARATOR, VR.FD);
                break;
            case OD:
                tagValue = converterDoublesToString(tag, attributes, DEFAULT_SEPARATOR, VR.OD);
                break;
            case SS:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.SS);
                break;
            case US:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.US);
                break;
            case AT:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.AT);
                break;
            case OL:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.OL);
                break;
            case SL:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.SL);
                break;
            case UL:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UL);
                break;
            case OW:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.OW);
                break;
            case OB:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.OB);
                break;
            case UN:
                tagValue = converterIntsToString(tag, attributes, DEFAULT_SEPARATOR, VR.UN);
                break;
            case SQ:
                tagValue = converterSequenceToString(tag, attributes);
                break;
            default:
                throw new IllegalArgumentException(String.format("VR={}，不支持该类型解析", vr.toString()));
        }
        return tagValue;
    }

    private static String converterSequenceToString(int tag, Attributes attributes) {
        List<Map<String, String>> sequenceValList = new ArrayList<>();
        Sequence sequence = attributes.getSequence(tag);
        Iterator<Attributes> iterator = sequence.iterator();
        while (iterator.hasNext()) {
            Map<String, String> sequenceValItemMap = new HashMap<>();
            sequenceValList.add(sequenceValItemMap);

            Attributes next = iterator.next();
            System.out.println(next.toString());
            int[] tags = next.tags();
            for (int childTags : tags) {
                // 避免死循环
                if (childTags == tag) {
                    continue;
                }
                sequenceValItemMap.put(childTags + "", getTagValue(childTags, next));
            }
        }
        return JSONUtil.toJson(sequenceValList, sequenceValList.getClass());
    }

    private static String converterStringsToString(int tag, Attributes attributes, String separator, VR vr) {
        String tagValue = null;
        String[] strArr = attributes.getStrings(null, tag, vr);
        if (null != strArr && strArr.length > 0) {
            tagValue = StringUtils.join(strArr, separator);
        }
        return tagValue;
    }

    private static String converterDoublesToString(int tag, Attributes attributes, String separator, VR vr) {
        String tagValue = null;
        double[] doubleArr = attributes.getDoubles(null, tag, vr);
        if (null != doubleArr && doubleArr.length > 0) {
            tagValue = getList(Arrays.stream(doubleArr).boxed().collect(Collectors.toList()), separator);
        }
        return tagValue;
    }

    private static String converterIntsToString(int tag, Attributes attributes, String separator, VR vr) {
        String tagValue = null;
        int[] intArr = attributes.getInts(null, tag, vr);
        if (null != intArr && intArr.length > 0) {
            tagValue = getList(Arrays.stream(intArr).boxed().collect(Collectors.toList()), separator);
        }
        return tagValue;
    }

    private static String converterHexToString(int tag, Attributes attributes, String separator) {
        int[] intArr = attributes.getInts(tag);
        List<String> hexList = new ArrayList<>();
        for (int val : intArr) {
            hexList.add(Integer.toHexString(val));
        }
        return getList(hexList, separator);
    }

    private static <T> String getList(List<T> list, String separator) {
        StringBuilder result = null;
        if (CollectionUtils.isNotEmpty(list)) {
            result = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                if (i == list.size() - 1) {
                    result.append(String.valueOf(list.get(i)));
                } else {
                    result.append(String.valueOf(list.get(i)) + separator);
                }
            }
        }
        return result == null ? null : result.toString();
    }
}
